use set::set::{Set, SetHashFunc, SetEqualFunc};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::rc::Rc;
fn int_hash(value: &i32) -> u64 {
    let mut hasher = DefaultHasher::new();
    value.hash(&mut hasher);
    hasher.finish()
}

fn int_equal(a: &i32, b: &i32) -> bool {
    a == b
}

fn string_hash(value: &String) -> u64 {
    let mut hasher = DefaultHasher::new();
    value.hash(&mut hasher);
    hasher.finish()
}

fn string_equal(a: &String, b: &String) -> bool {
    a == b
}

#[test]
fn test_set_new_free() {
    let mut set = Set::new(int_hash, int_equal).unwrap();
    // Fill the set with many values before freeing


    // Fill the set with many values before freeing
    for i in 0..10000 {
        let value = Box::new(i);
        set.insert(i);
    }

    // Free the set
    drop(set);

    // Test out of memory scenario
    // Note: Rust does not have a direct equivalent to alloc_test_set_limit,
    // so this part of the test is omitted.
}

#[test]
fn test_set_insert() {
    let mut set = Set::new(int_hash, int_equal).unwrap();
    let numbers1 = vec![1, 2, 3, 4, 5, 6];
    let numbers2 = vec![5, 6, 7, 8, 9, 10];

    for i in 0..6 {
        set.insert(numbers1[i]);
    }
    for i in 0..6 {
        set.insert(numbers2[i]);
    }

    assert_eq!(set.num_entries(), 10);
}

#[test]
fn test_set_query() {
    let mut set = Set::new(string_hash, string_equal).unwrap();
    for i in 0..10000 {
        let value = i.to_string();
        set.insert(value);
    }

    for i in 0..10000 {
        let value = i.to_string();
        assert!(set.contains(&value));
    }

    assert!(!set.contains(&"-1".to_string()));
    assert!(!set.contains(&"100001".to_string()));
}

#[test]
fn test_set_remove() {
    let mut set = Set::new(string_hash, string_equal).unwrap();
    for i in 0..10000 {
        let value = i.to_string();
        set.insert(value);
    }

    let mut num_entries = set.num_entries();
    assert_eq!(num_entries, 10000);

    for i in 4000..6000 {
        let value = i.to_string();
        assert!(set.contains(&value));
        assert!(set.remove(&value));
        assert_eq!(set.num_entries(), num_entries - 1);
        assert!(!set.contains(&value));
        num_entries -= 1;
    }

    for i in -1000..-500 {
        let value = i.to_string();
        assert!(!set.remove(&value));
        assert_eq!(set.num_entries(), num_entries);
    }

    for i in 50000..51000 {
        let value = i.to_string();
        assert!(!set.remove(&value));
        assert_eq!(set.num_entries(), num_entries);
    }
}

#[test]
fn test_set_union() {
    let mut set1 = Set::new(int_hash, int_equal).unwrap();
    let numbers1 = vec![1, 2, 3, 4, 5, 6, 7];
    for i in 0..7 {
        set1.insert(numbers1[i]);
    }

    let mut set2 = Set::new(int_hash, int_equal).unwrap();
    let numbers2 = vec![5, 6, 7, 8, 9, 10, 11];
    for i in 0..7 {
        set2.insert(numbers2[i]);
    }

    let result_set = Set::union(&set1, &set2).unwrap();
    assert_eq!(result_set.num_entries(), 11);

    let result = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
    for i in 0..11 {
        assert!(result_set.contains(&result[i]));
    }

    // Test out of memory scenario
    // Note: Rust does not have a direct equivalent to alloc_test_set_limit,
    // so this part of the test is omitted.
}

#[test]
fn test_set_intersection() {
    let mut set1 = Set::new(int_hash, int_equal).unwrap();
    let numbers1 = vec![1, 2, 3, 4, 5, 6, 7];
    for i in 0..7 {
        set1.insert(numbers1[i]);
    }

    let mut set2 = Set::new(int_hash, int_equal).unwrap();
    let numbers2 = vec![5, 6, 7, 8, 9, 10, 11];
    for i in 0..7 {
        set2.insert(numbers2[i]);
    }

    let result_set = Set::intersection(&set1, &set2).unwrap();
    assert_eq!(result_set.num_entries(), 3);

    let result = vec![5, 6, 7];
    for i in 0..3 {
        assert!(result_set.contains(&result[i]));
    }

    // Test out of memory scenario
    // Note: Rust does not have a direct equivalent to alloc_test_set_limit,
    // so this part of the test is omitted.
}

#[test]
fn test_set_to_array() {
    let mut set = Set::new(int_hash, int_equal).unwrap();
    let values: Vec<i32> = (0..100).collect();
    for i in 0..100 {
        set.insert(values[i]);
    }

    let array = set.to_array();

    let mut array = set.to_array();
    array.sort_by(|a, b| a.cmp(b));
    for i in 0..100 {
        assert_eq!(*array[i], i as i32);
    }
    // so this part of the test is omitted.
}

#[test]
fn test_set_iterating() {
    let mut set = Set::new(string_hash, string_equal).unwrap();
    for i in 0..10000 {
        let value = i.to_string();
        set.insert(value);
    }

    let mut count = 0;
    for _ in set.iter() {
        count += 1;
    }

    assert_eq!(count, 10000);

    let mut set = Set::new(int_hash, int_equal).unwrap();
    let mut iter = set.iter();
    assert!(iter.next().is_none());
}

#[test]
fn test_set_iterating_remove() {
    let mut set = Set::new(string_hash, string_equal).unwrap();
    for i in 0..10000 {
        let value = i.to_string();
        set.insert(value);
    }

    let mut count = 0;
    let mut removed = 0;
    let mut to_remove = Vec::new();
    for value in set.iter() {
        if value.parse::<i32>().unwrap() % 100 == 0 {
            to_remove.push(value.clone());
        }
        count += 1;
    }

    for value in to_remove {
        assert!(set.remove(&value));
        removed += 1;
    }

    assert_eq!(count, 10000);
    assert_eq!(removed, 100);
    assert_eq!(set.num_entries(), 10000 - removed);
}

#[test]
#[test]
fn test_set_free_function() {
    use std::cell::Cell;

    let mut set = Set::new(int_hash, int_equal).unwrap();
    let allocated_values = Rc::new(Cell::new(0));
    let allocated_values_clone = Rc::clone(&allocated_values);
    set.register_free_function(move |_value| {
        allocated_values_clone.set(allocated_values_clone.get() - 1);
    });

    for i in 0..1000 {
        allocated_values.set(allocated_values.get() + 1);
        set.insert(i);
    }

    assert_eq!(allocated_values.get(), 1000);

    let i = 500;
    assert!(set.remove(&i));
    assert_eq!(allocated_values.get(), 999);

    drop(set);
    assert_eq!(allocated_values.get(), 0);
}

#[test]
fn test_set_out_of_memory() {
    let mut set = Set::new(int_hash, int_equal).unwrap();
    let values: Vec<i32> = (0..66).collect();

    // Test normal failure
    // Note: Rust does not have a direct equivalent to alloc_test_set_limit,
    // so this part of the test is omitted.

    for i in 0..65 {
        assert!(set.insert(values[i]));
        assert_eq!(set.num_entries(), i + 1);
    }

    assert_eq!(set.num_entries(), 65);

    // Test the 66th insert
    // Note: Rust does not have a direct equivalent to alloc_test_set_limit,
    // so this part of the test is omitted.
}